/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/graph/IfSelector.h"

namespace alkes {

IfSelector::IfSelector()
: exp_(0)
, last_render_(AL_LAST_RENDER_NONE)
{
}

IfSelector::~IfSelector()
{
}

bool IfSelector::hasVisibleChild() const
{
    Element* e = selectChild();

    return e && e->isVisible();
}

Composite::Iterator IfSelector::getLastChild()
{
    Variable* var = exp_;
    if (!var)
        return false;

    bool exp = var->eval();

    Element* child = selectChild(exp);
    if (child)
    {
        Iterator last_child = exp ? &true_ : &false_;
        last_child->setPrev(0);
        return last_child;
    }

    return 0;
}

void IfSelector::setTrueElement(Element* v)
{
    if (this->isModifiable())
    {
        true_.setObject(v);
        setDirty(true);
    }
}

void IfSelector::setFalseElement(Element* v)
{
    if (this->isModifiable())
    {
        false_.setObject(v);
        setDirty(true);
    }
}

void IfSelector::setExpression(Variable* exp)
{
    if (exp == exp_)
        return ;

    if (this->isModifiable())
    {
        exp_.registerResource(this, exp);
        setDirty(true);
    }
}

bool IfSelector::onPreRender(GraphicsContext& /*gc*/)
{
    Variable* var = exp_;
    if (!var)
        return false;

    bool exp = var->eval();
    if (exp)
        last_render_ = AL_LAST_RENDER_TRUE;
    else
        last_render_ = AL_LAST_RENDER_FALSE;

    return true;
}

bool IfSelector::onPreUpdate(UpdateContext& uc)
{
    Variable* var = exp_;
    if (!var)
        return false;

    bool exp = var->eval();

    AL_LOCK_SELF();

    bool change_dirty_mode = last_render_ != AL_LAST_RENDER_NONE
        && last_render_ != (exp ? AL_LAST_RENDER_TRUE : AL_LAST_RENDER_FALSE);

    restore_dirty_mode_ = change_dirty_mode ? uc.setForceDirty(true) : false;

    if (change_dirty_mode)
    {
        reverse_iter = &true_;
        true_.setPrev(&false_);
        false_.setPrev(0);
    }
    else
    {
        reverse_iter = exp ? &true_ : &false_;
        reverse_iter->setPrev(0);
    }

    return true;
}

bool IfSelector::onPostUpdate(UpdateContext& uc, bool updated_child)
{
    if (restore_dirty_mode_)
        uc.setForceDirty(false);

    AL_UNLOCK_SELF();

    return Selector::onPostUpdate(uc, updated_child);
}

Element* IfSelector::selectChild() const
{
    Variable* exp = exp_;
    if (!exp)
        return 0;

    return selectChild(exp->eval());
}

Element* IfSelector::selectChild(bool exp) const
{
    if (exp)
        return iter_cast<Element*>(true_);
    else
        return iter_cast<Element*>(false_);
}

}

